package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.CourseBaseDTO;
import com.xuecheng.api.content.model.qo.QueryCourseModel;
import com.xuecheng.content.common.domain.code.CommonErrorCode;
import com.xuecheng.content.common.domain.code.ContentErrorCode;
import com.xuecheng.content.common.domain.page.PageRequestParams;
import com.xuecheng.content.common.domain.page.PageVO;
import com.xuecheng.content.common.enums.common.CommonEnum;
import com.xuecheng.content.common.enums.content.CourseAuditEnum;
import com.xuecheng.content.common.enums.content.CourseChargeEnum;
import com.xuecheng.content.common.exception.ExceptionCast;
import com.xuecheng.content.common.util.StringUtil;
import com.xuecheng.content.controller.CourseAuditController;
import com.xuecheng.content.convert.CourseBaseConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.CourseMarket;
import com.xuecheng.content.mapper.CourseBaseMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.CourseMarketService;
import com.xuecheng.content.service.CoursePubService;
import com.xuecheng.content.service.TeachplanService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;

/**
 * <p>
 * 课程基本信息 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class CourseBaseServiceImpl extends ServiceImpl<CourseBaseMapper, CourseBase> implements CourseBaseService{


    @Autowired
    private CourseMarketService courseMarketService;

    @Autowired
    private TeachplanService teachplanService;

    @Autowired
    private CoursePubService pubService;

    @Override
    public List<CourseBase> queryAll() {
        List<CourseBase> result = list();
        return result;
    }

    @Override
    public PageVO queryCourseList(PageRequestParams params, QueryCourseModel model,long companyId) {

        //1.判断分页的数据
        if (params.getPageNo()<1){
            params.setPageNo(PageRequestParams.DEFAULT_PAGE_NUM);
        }
        if (params.getPageSize()<1){
            params.setPageSize(PageRequestParams.DEFAULT_PAGE_SIZE);
        }
        //2.添加条件查询
        LambdaQueryWrapper<CourseBase> wrapper = new LambdaQueryWrapper<>();
        //判断条件查询（条件查询是否有数据，有数据添加条件）
        if (StringUtil.isNotBlank(model.getCourseName())){
            //根据课程名称模糊查询
            wrapper.like(CourseBase::getName,model.getCourseName());
        }
        //判断课程的状态
        if (StringUtil.isNotBlank(model.getAuditStatus())){
            //设置课程的条件
            wrapper.eq(CourseBase::getAuditStatus,model.getAuditStatus());
        }
        //判断机构的id 是否相等，相等的话，就不走这个wrapper.eq(CourseBase::getCompanyId,companyId);
        //不相等的话走查询所有机构的id，
        if (!(ObjectUtils.nullSafeEquals(companyId, CourseAuditController.OPERATION_FLAG))){
            wrapper.eq(CourseBase::getCompanyId,companyId);
        }
        wrapper.eq(CourseBase::getStatus,CommonEnum.USING_FLAG.getCodeInt());


        //3.创建分页数据
        Page<CourseBase> page = new Page<>(params.getPageNo(),params.getPageSize());
        //4.根据分页和条件查询list数据
        Page<CourseBase> pageResult = this.page(page, wrapper);
        //获取list集合
        List<CourseBase> courseBaseList = pageResult.getRecords();
        //获取总条数
        long total = pageResult.getTotal();

        // TODO: 2021/12/9
        //List<CourseBaseDTO> dtos;
        List<CourseBaseDTO> dtos = Collections.emptyList();
        //判断是否为空
        if (!(CollectionUtils.isEmpty(courseBaseList))) {
            dtos = CourseBaseConvert.INSTANCE.entitys2dtos(courseBaseList);
        }

        // 6.封装PageVO数据
        PageVO pageVO = new PageVO(dtos,total,params.getPageNo(),params.getPageSize());

        return pageVO;
    }

    @Override
    @Transactional
    public CourseBaseDTO createCourseBase(CourseBaseDTO dto) {
        //业务分析：
        //  1.是否要开启事务
        //     如果是增删改操作，必须要开启事务
        //  2.判断关键数据
        //    有此数据才可以进行操作，一般有前端来传入
        //      来源：
        //          数据库表约束
        //          前端页面的表单说明
        //              为必填项：红星
        //          api接口文档（Yapi）
        //判断机构id
        verifyCourseMsg(dto);
        //  3.判断业务数据（添加没有业务数据判断）
        //    系统中存在的数据，如果数据状态有问题，当前操作要终止，一般数据来源于后端

        //  4.判断数据是否要被赋值（默认值）
        //      status：数据库已经赋值，无需操作
        //      审核状态 auditStatus：新添加的课程默认审核状态--未提交（状态码）
        dto.setAuditStatus(CourseAuditEnum.AUDIT_UNPAST_STATUS.getCode());

        //  5.将dto转为po数据
        //      因为mp的api只能对po数据进行操作
        CourseBase courseBase = CourseBaseConvert.INSTANCE.dto2po(dto);
        //  6.保存数据并返回结果，判断结果是否成功
        boolean baseResult = this.save(courseBase);
        if (!baseResult){
            throw new RuntimeException("课程新增失败");
        }

        //  6.2 课程营销信息
        CourseMarket courseMarket = new CourseMarket();
        //设置id,因为和课程信息表关联
        courseMarket.setCourseId(courseBase.getId());
        //设置课程
        courseMarket.setCharge(dto.getCharge());
        //对收费课程进行数据上的判断
        // 判断课程是否收费：
        // 1.如果是免费，无需给price赋值
        if(CourseChargeEnum.CHARGE_YES.getCode().equals(dto.getCharge())){
            // 2.如果是收费，price必须有值
            if (ObjectUtils.isEmpty(dto.getPrice())){
                throw new RuntimeException("课程价格不能为空");
            }
            courseMarket.setPrice(dto.getPrice().floatValue());
        }
        //调用service的方法
        boolean marketResult = courseMarketService.save(courseMarket);
        //判断courseResult是否新增成功
        if (!marketResult){
            throw new RuntimeException("保存课程营销失败");
        }
        // 7.如果操作成功，将数据转为dto并返回
        // 将数据库中的最新数据转为dto
        Long id = courseBase.getId();
        CourseBaseDTO resultDTO = getLastCourseBaseDTO(dto, id);

        return resultDTO;
    }

    private CourseBaseDTO getLastCourseBaseDTO(CourseBaseDTO dto, Long id) {
        CourseBase po = this.getById(id);

        CourseBaseDTO resultDTO = CourseBaseConvert.INSTANCE.entity2dto(po);
        resultDTO.setCharge(dto.getCharge());
        resultDTO.setPrice(dto.getPrice());
        return resultDTO;
    }


    private void verifyCourseMsg(CourseBaseDTO dto) {
        if (ObjectUtils.isEmpty(dto.getCompanyId())){
            throw new RuntimeException("机构id不能为空");
        }
        //判断课程等级
        if (StringUtil.isBlank(dto.getGrade())){
            throw new RuntimeException("课程等级不能为空");
        }
        //判断大分类
        if (StringUtil.isBlank(dto.getMt())){
            throw new RuntimeException("大分类不能为空");
        }
        //判断课程名称
        if (StringUtil.isBlank(dto.getName())){
            throw new RuntimeException("课程名称不能为空");
        }
        //判断小分类
        if (StringUtil.isBlank(dto.getSt())){
            throw new RuntimeException("小分类不能为空");
        }
        //判断课程的方式zhibo，点播，录播
        if (StringUtil.isBlank(dto.getTeachmode())){
            throw new RuntimeException("课程的方式不能为空");
        }
        //判断适用人群
        if (StringUtil.isBlank(dto.getUsers())) {
            throw  new RuntimeException("适用人群不能为空");
        }
        //判断收费规则
        if (StringUtil.isBlank(dto.getCharge())){
            throw  new RuntimeException("课程价格不能为空");
        }
    }

    @Override
    //=====================修改======根据id查询====业务=====
    //业务分析：
    //  1.判断关键数据
    //      courseBaseId  companyI
    //  2.判断业务数据
    //      课程基础信息
    //          判断是否存在
    //          判断是否是同一家教学机构
    //          判断是否删除
    //      课程营销
    //  3.查询数据并转为dto返回
    public CourseBaseDTO getCourseBase(Long courseBaseId,Long companyId) {

        //业务分析：
        //  1.判断关键数据
        //      courseBaseId  companyI
        if(ObjectUtils.isEmpty(courseBaseId)||ObjectUtils.isEmpty(companyId)){
            //如果为空的话就返回错误信息
            throw new RuntimeException("传入参数不匹配");
        }
        CourseBase courseBase = getCourseBaseByBaseId(courseBaseId, companyId);
        CourseMarket courseMarket = getCourseMarketByCourseId(courseBaseId);

        //  3.查询数据并转为dto返回
        CourseBaseDTO courseBaseDTO = CourseBaseConvert.INSTANCE.entity2dto(courseBase);
        courseBaseDTO.setPrice(new BigDecimal(courseMarket.getPrice()));
        courseBaseDTO.setCharge(courseMarket.getCharge());
        return courseBaseDTO;
    }


    private CourseMarket getCourseMarketByCourseId(Long courseBaseId) {
        //          判断是否是同一家教学机构
        LambdaQueryWrapper<CourseMarket> marketLambdaQueryWrapper = new LambdaQueryWrapper<>();
        marketLambdaQueryWrapper.eq(CourseMarket::getCourseId, courseBaseId);
        //查询课程营销
        CourseMarket courseMarket = courseMarketService.getOne(marketLambdaQueryWrapper);

//        判断是否存在
        if (ObjectUtils.isEmpty(courseMarket)){
            throw new RuntimeException("课程营销数据不存在");
        }
        return courseMarket;
    }

    private CourseBase getCourseMarketByCourseId(long courseBaseId, Long companyId) {
        //  2.判断业务数据
        //      课程基础信息
        LambdaQueryWrapper<CourseBase> baseQueryWrapper = new LambdaQueryWrapper<>();
        baseQueryWrapper.eq(CourseBase::getId, courseBaseId);
        baseQueryWrapper.eq(CourseBase::getCompanyId, companyId);
        //查询出来课程的数据
        CourseBase courseBase = this.getOne(baseQueryWrapper);

        if (ObjectUtils.isEmpty(courseBase)){
            //          判断是否存在
            throw new RuntimeException("课程不存在");
        }
        //          判断是否删除
        Integer status = courseBase.getStatus();
        if (!CommonEnum.USING_FLAG.getCodeInt().equals(status)){
            throw new RuntimeException("课程已经被删除");
        }
        return courseBase;
    }

    //业务分析：
    //  1.判断关键数据
    //      可以使用添加的判断
    //      修改时：课程id是必须存在
    //  2.判断业务数据
    //      课程基础信息
    //          判断是否存在
    //          判断是否是同一家教学机构
    //          判断是否删除
    //          判断课程审核状态：未提交、审核未通过
    //      课程营销
    //  3.修改数据内容
    //      课程基础信息表
    //          有些内容是不容修改的：courseBaseId、companyid、auditstatus、status
    //      课程营销数据表
    //          charge price
    //       修改数据时要判断修改后结果
    //  4.返回前端最新的数据库的数据并封装为dto返回
    @Override
    @Transactional
    public CourseBaseDTO modifyCourseBase(CourseBaseDTO dto) {
        //业务分析：
        //  1.判断关键数据
        //      可以使用添加的判断
        verifyCourseMsg(dto);

        //      修改时：课程id是必须存在
        Long courseId = dto.getCourseBaseId();
        if (ObjectUtils.isEmpty(courseId)){
            throw new RuntimeException("课程的id不能为空");
        }

        //  2.判断业务数据
        //      课程基础信息
        //          判断课程是否存在
        //          判断是否是同一家教学机构
        //          判断是否删除
        //          判断课程审核状态：未提交、审核未通过
        Long companyId = dto.getCompanyId();
        CourseBase courseBase = getCourseBaseByBaseId(courseId, companyId);
        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
        ) {
            throw new RuntimeException("课程审核状态异常");
        }

        //      课程营销
        //先判断课程营销是否存在，如果不存在就不能修改
        CourseMarket courseMarket = getCourseMarketByCourseId(courseId);
        //  3.修改数据内容
        //      课程基础信息表
        //          有些内容是不容修改的：courseBaseId、companyid、auditstatus、status
        //      课程营销数据表
        //          charge price
        //       修改数据时要判断修改后结果
        //修改是要转为po，因为mybatis plus
        CourseBase po = CourseBaseConvert.INSTANCE.dto2po(dto);
        //为了防止前端乱修改数据
        //机构id
        po.setCompanyId(null);
        po.setAuditStatus(null);
        po.setStatus(null);

        //修改
        boolean baseResult = this.updateById(po);
        //
        if (!baseResult){
            throw new RuntimeException("课程修改失败");
        }
        //     课程营销数据表
        //         charge price
        // 如果使用mq修改一张表中的极个别数据，UpdateWrapper
        // update course_market set xx=xxx,xxx=xxx  where courseid=xx
        LambdaUpdateWrapper<CourseMarket> marketUpdateWrapper = new LambdaUpdateWrapper<>();

        marketUpdateWrapper.set(CourseMarket::getCharge,dto.getCharge());
        //判断是否收费

        if (CourseChargeEnum.CHARGE_YES.getCode().equals(dto.getCharge())){
            //如果是收费的,就判断收费的金额，是否为空
            if (ObjectUtils.isEmpty(dto.getPrice())){
                throw new RuntimeException("收费课程不能为空");
            }
            marketUpdateWrapper.set(CourseMarket::getPrice,dto.getPrice().floatValue());
        }
        marketUpdateWrapper.eq(CourseMarket::getCourseId, dto.getCourseBaseId());

        //调用方法courseMarketService
        boolean marketResult = courseMarketService.update(marketUpdateWrapper);
        //      修改数据时要判断修改后结果
        if (!marketResult){
            throw new RuntimeException("修改课程信息失败");
        }
        //      修改数据时要判断修改后结果

        // 4.返回前端最新的数据库的数据并封装为dto返回
        CourseBaseDTO resultDTO = getLastCourseBaseDTO(dto, courseId);

        return resultDTO;
    }




    //根据id查询判断业务基础信息，以及状态
    private CourseBase getCourseBaseByBaseId(Long courseBaseId, Long companyId) {
        LambdaQueryWrapper<CourseBase> baseQueryWrapper = new LambdaQueryWrapper<>();
        baseQueryWrapper.eq(CourseBase::getId, courseBaseId);
        baseQueryWrapper.eq(CourseBase::getCompanyId, companyId);
        // baseQueryWrapper.eq(CourseBase::getStatus, CommonEnum.USING_FLAG.getCodeInt());

        CourseBase courseBase = this.getOne(baseQueryWrapper);

        if (ObjectUtils.isEmpty(courseBase)) {
            throw new RuntimeException("课程不存在");
        }

        Integer status = courseBase.getStatus();
        if (!(CommonEnum.USING_FLAG.getCodeInt().equals(status))) {
            throw new RuntimeException("课程信息已经被删除");
        }
        return courseBase;
    }

    /*
    删除课程
     */

    @Transactional
    public void removeCourseBase(Long courseBaseId,Long companyId) {
        //判断关键数据的有效性---就是判断前端传过来的值
        if (ObjectUtils.isEmpty(companyId) || ObjectUtils.isEmpty(courseBaseId)){
            throw new RuntimeException("课程id和机构id不存在");
        }
        //2.根据Id获得课程基础信息并进行相应的判断
        //2.1 获得课程基础信息
        CourseBase courseBase = this.getById(courseBaseId);
        //判断数据是否存在
        if (ObjectUtils.isEmpty(courseBase)){
            throw new RuntimeException("该数据不存在");
        }
//        //判断是否一家机构
//        if (companyId!=courseBase.getCompanyId()){
//            throw new RuntimeException("无权访问数据");
//        }
        // 判断是否是同一家机构
        if (!(ObjectUtils.nullSafeEquals(companyId, courseBase.getCompanyId()))) {
            ExceptionCast.cast(CommonErrorCode.E_100108);
        }
        //判断课程的审核状态
        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
        ) {
            throw new RuntimeException("此状态不能删除");
        }
        //修改课程信息状态值
        LambdaUpdateWrapper<CourseBase> baseLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        //逻辑删除 ，设置成DELETE_FLAG(0,"删除态"),
        baseLambdaUpdateWrapper.set(CourseBase::getStatus,CommonEnum.DELETE_FLAG.getCodeInt());
        //删除修改的时间
        baseLambdaUpdateWrapper.set(CourseBase::getChangeDate, LocalDateTime.now());

        baseLambdaUpdateWrapper.eq(CourseBase::getId,courseBaseId);

        boolean update = this.update(baseLambdaUpdateWrapper);
        //判断是否删除成功
        if (!update){
            throw new RuntimeException("删除失败");
        }
    }

    /*
    机构提交课程业务分析
    1.判断前端是否为空
    2.判断业务上的数据
        判断这条课程是否存在
        是否删除
        判断课程状态是否具有提交审核
        判断是否在同一家机构

    3.提交数据，判断提交或者拒绝的信息是否填写
    4.修改状态，以提交

     */
    @Override
    public void commitCourseBase(Long courseBaseId, Long companyId) {
        //1.判断前端是否为空
        if (ObjectUtils.isEmpty(courseBaseId) || ObjectUtils.isEmpty(companyId)){
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        //    2.判断业务上的数据
        //        判断这条课程是否存在
        //        是否删除
        //        判断课程状态是否具有提交审核
        //        判断是否在同一家机构
        //
        //获取courseBase
        LambdaQueryWrapper<CourseBase> courseBaseQuery = new LambdaQueryWrapper<>();
        courseBaseQuery.eq(CourseBase::getCompanyId,companyId);
        courseBaseQuery.eq(CourseBase::getId,courseBaseId);
        //查询出来数据
        CourseBase courseBase1 = this.getOne(courseBaseQuery);
        //        判断课程是否存在
        if (ObjectUtils.isEmpty(courseBase1)){
            throw new RuntimeException("课程不存在");
        }
        //判断是否删除
        Integer status = courseBase1.getStatus();
        if (!CommonEnum.USING_FLAG.getCodeInt().equals(status)){
            throw new RuntimeException("课程已经被删除了");
        }
        //          判断课程审核状态：未提交、审核未通过
        String auditStatus = courseBase1.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
        ) {
            throw new RuntimeException("课程审核状态异常");
        }

        //    3.提交数据，判断提交或者拒绝的信息是否填写
//        createDate  changeDate 使用MP自动填充来完成
//        UpdateWrapper 会将MP的自动填充功能失效
        //设置条件
        LambdaUpdateWrapper<CourseBase> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(CourseBase::getAuditStatus,CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode());
        updateWrapper.set(CourseBase::getChangeDate, LocalDateTime.now());
        updateWrapper.eq(CourseBase::getId, courseBaseId);

//        执行修改
        //    4.修改状态，以提交
        boolean update = this.update(updateWrapper);
        //判断是否修改成功
        if (!update){
            ExceptionCast.cast(ContentErrorCode.E_120018);
        }

    }

    /*
    运营审核
    业务分析
    1.判断关键数据
    auditMind  auditStatus  courseid
    2.判断业务数据
        课程基础信息
            判断是否存在
            判断是否删除
            判断审核状态
                必须为：已提交
        审核状态
            运营平台只能给课程审核状态赋值：审核通过、审核未通过
    3.修改课程审核信息
    auditMind  auditStatus  auditNum（每次+1）
     */
    @Override
    public void approve(CourseBaseDTO dto) {
        //1.判断关键数据
        //    auditMind  auditStatus  courseid
        if (ObjectUtils.isEmpty(ObjectUtils.isEmpty(dto.getCourseBaseId())
                || ObjectUtils.isEmpty(dto.getAuditStatus())
                || ObjectUtils.isEmpty(dto.getAuditMind())))
        {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        //2.判断业务数据
        //        课程基础信息
        //            判断是否存在
        //            判断是否删除
        //            判断审核状态
        //                必须为：已提交
        //        审核状态
        //            运营平台只能给课程审核状态赋值：审核通过、审核未通过
        LambdaQueryWrapper<CourseBase> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CourseBase::getId,dto.getCourseBaseId());
        wrapper.eq(CourseBase::getStatus,CommonEnum.USING_FLAG.getCodeInt());
        wrapper.eq(CourseBase::getAuditStatus,CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode());

        int count = this.count(wrapper);
        if (count < 1) {
            ExceptionCast.cast(ContentErrorCode.E_120416);
        }

        //审核状态
        //运营平台只能给课程审核状态赋值：审核通过、审核未通过

        String auditStatus = dto.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PUBLISHED_STATUS.equals(auditStatus)
            || CourseAuditEnum.AUDIT_COMMIT_STATUS.equals(auditStatus)
            || CourseAuditEnum.AUDIT_UNPAST_STATUS.equals(auditStatus))
        {
            ExceptionCast.cast(ContentErrorCode.E_120016);
        }
        // 3.修改课程审核信息
        //    auditMind  auditStatus  auditNum（每次+1）
        LambdaUpdateWrapper<CourseBase> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(CourseBase::getId,dto.getCourseBaseId());
        updateWrapper.set(CourseBase::getAuditMind,dto.getAuditMind());
        updateWrapper.set(CourseBase::getAuditStatus,dto.getAuditStatus());
        //使用redis来实现 auditNum（每次+1）
        updateWrapper.set(CourseBase::getAuditNums,dto.getAuditNums());
        updateWrapper.set(CourseBase::getChangeDate,LocalDateTime.now());

        boolean update = this.update(updateWrapper);
        //判断结果
        if (!update){
            ExceptionCast.cast(ContentErrorCode.E_120017);
        }


    }



}
